/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the Contiki operating system.
 *
 */

#include "contiki.h"
#include "net/netstack.h"
#include "random.h"
#include "ffz/ffzconfig.h"
#include "ffz/raft.h"
#include "dev/button-sensor.h"
#include <string.h>

#include "sys/log.h"
#define LOG_MODULE "App"
#define LOG_LEVEL LOG_LEVEL_INFO

#define WITH_SERVER_REPLY 1
#define UDP_CLIENT_PORT 8765
#define UDP_SERVER_PORT 5678

#define RAFT_NODE_NUM 3

static struct simple_udp_connection recv_conn;
static struct simple_udp_connection send_conn;

PROCESS(udp_server_process, "UDP server");
PROCESS(btn_process, "Button Service");
PROCESS(candidate_process, "Button Service");

AUTOSTART_PROCESSES(&udp_server_process, &btn_process, &candidate_process);

/*---------------------------------------------------------------------------*/
static void
udp_rx_callback(struct simple_udp_connection *c,
                const uip_ipaddr_t *sender_addr,
                uint16_t sender_port,
                const uip_ipaddr_t *receiver_addr,
                uint16_t receiver_port,
                const uint8_t *data,
                uint16_t datalen)
{
  LOG_INFO("Received request '%.*s' from ", datalen, (char *)data);
  LOG_INFO_6ADDR(sender_addr);
  LOG_INFO_("\n");
  //   /* send back the same string to the client as an echo reply */
  //   LOG_INFO("Sending response.\n");
  //   simple_udp_sendto(&recv_conn, data, datalen, sender_addr);
  raftHandler(sender_addr, data, datalen);
}
/*---------------------------------------------------------------------------*/
static void
udp_tx_callback(struct simple_udp_connection *c,
                const uip_ipaddr_t *sender_addr,
                uint16_t sender_port,
                const uip_ipaddr_t *receiver_addr,
                uint16_t receiver_port,
                const uint8_t *data,
                uint16_t datalen)
{
  LOG_INFO("Received request '%.*s' from ", datalen, (char *)data);
  LOG_INFO_6ADDR(sender_addr);
  LOG_INFO_("\n");
#if WITH_SERVER_REPLY
  /* send back the same string to the client as an echo reply */
  LOG_INFO("Sending response.\n");
  // simple_udp_sendto(&send_conn, data, datalen, sender_addr);
#endif /* WITH_SERVER_REPLY */
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(udp_server_process, ev, data)
{
  PROCESS_BEGIN();

  raftNodeTableInit(RAFT_NODE_NUM);

  /* Initialize DAG root */
  NETSTACK_ROUTING.root_start();

  /* Initialize UDP connection */
  simple_udp_register(&recv_conn, UDP_SERVER_PORT, NULL,
                      UDP_CLIENT_PORT, udp_rx_callback);
  /* Initialize UDP connection */
  simple_udp_register(&send_conn, UDP_CLIENT_PORT, NULL,
                      UDP_SERVER_PORT, udp_tx_callback);

  /* set the simple connection to the rpl-packet module */
  initSendConnection(&send_conn);
  /* set the simple connection to the rpl-packet module */
  initRecvConnection(&recv_conn);

  printf("udp_server_process started......\n");
  print_local_addresses_and_set_id();
  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(btn_process, ev, data)
{
  static struct etimer button_timer;
  // static char send_data[32];
  // uip_ipaddr_t dest_ipaddr;
  PROCESS_BEGIN();

  /* Setup a periodic timer that expires after 10 seconds. */
  etimer_set(&button_timer, CLOCK_SECOND * 4);
  SENSORS_ACTIVATE(button_sensor);
  printf("btn_process started......\n");
  while (1)
  {
    PROCESS_YIELD();
    if (ev == EVENT_BUTTON)
    {
      int count = button_click();
      if (count == 1)
      {
        // printf("RESET TIMER\n");
        etimer_restart(&button_timer);
        // process_post(&candidate_process, EVENT_CANDIDATE_RESET, NULL);
      }
      printf("action select %s\n", get_action_name(count));
    }
    else if (ev == EVENT_ETIMER)
    {
      int count = button_clear();
      if (count != 0)
      {
        printf("action use %s\n", get_action_name(count));
        // snprintf(send_data, sizeof(send_data), "hello my action is %d", count);
        // simple_udp_sendto(&send_conn, send_data, strlen(send_data), &getRaftNodeTableItem(0)->ip);
        etimer_reset(&button_timer);
      }
    }
  }
  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(candidate_process, ev, data)
{
  static struct etimer candidate_timer;
  PROCESS_BEGIN();
  printf("candidate_process started......\n");

  /* Setup a periodic timer that expires after 3 seconds. */
  etimer_set(&candidate_timer, CLOCK_SECOND * 3);
  initCandidateClockUnit(3);
  while (1)
  {
    PROCESS_YIELD();
    if (ev == EVENT_ETIMER)
    {
      int clock = refreshCandidateClock();
      if (clock > getTriggerClock())
      {
        TIMER_BecomeCandiate();
        resetClockCount(1);
      }
      etimer_restart(&candidate_timer);
    }
    else if (ev == EVENT_CANDIDATE_RESET)
    {
      resetClockCount(1);
      etimer_restart(&candidate_timer);
    }
  }
  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
